#ifndef CODE_LINES_BUILDER_CPP
#define CODE_LINES_BUILDER_CPP

#include <fstream>
#include <sstream>
#include <string>
#include <map>

#include "stdafx.h"
#include "UmlExt.h"
#include "Code_Line.h"
#include "Code_Line_Builder.h"
#include "Library_Functions.h"

GOAL::Code_Line_Builder::Code_Line_Builder(const Udm::Object& parent,
                                             const std::string & target_dir,
                                             const std::string & type)
: code_lines_ (parent), target_dir_ (target_dir), type_ (type), tabs_ (0),
  header_ (std::stringstream::in | std::stringstream::out),
  includes_ (std::stringstream::in | std::stringstream::out),
  source_ (std::stringstream::in | std::stringstream::out)
{
/*  std::string filename (target_dir + "/" + 
       GOAL::convertToDirectory ( parent.getPath2 (".",false)));


  if (GOAL::Grammar::isClass (parent.type ()))
    {
      if (type == "C++")
        {
          header_.open (std::string (filename + ".h").c_str ());
          source_.open (std::string (filename + ".cpp").c_str ());
        }
      else if (type == "Java")
        {
          source_.open (std::string (filename + ".java").c_str ());
        }
    }
  else
    {
      try
        {
          GOAL::CodeAdapter code = GOAL::CodeAdapter::Cast (parent);

          std::string temp1 = code.getPath2 (".",false);
          std::string temp2 = GOAL::getParentPath (temp1);
          std::string temp3 = GOAL::convertToDirectory (temp2);

          std::string directory (target_dir + "/" + temp3);

     //       {
     //         std::string message ("code adapter directory should be " +
     //           directory);
     //         AfxMessageBox(message.c_str ());
     //       }

          GOAL::createDirectory (directory);
  
          // treat this as just a code segment - no header file
          if (type == "C++")
            {

              source_.open (std::string (filename + ".cpp").c_str ());
            }
          else if (type == "Java")
            {
              source_.open (std::string (filename + ".java").c_str ());
            }
        }
      // catch the exception regardless of type and discard it.
      catch (...)
        {

        }
    }*/
}

GOAL::Code_Line_Builder::~Code_Line_Builder()
{
 // header_.close ();
 // source_.close ();
}

int 
GOAL::Code_Line_Builder::get_num_lines ()
{
  return code_lines_.size ();
}

void 
GOAL::Code_Line_Builder::set_target_directory (std::string & target_dir)
{
  target_dir_ = target_dir;
}

void
GOAL::Code_Line_Builder::set_parent (const Udm::Object & parent)
{
  code_lines_.set_parent (parent);
}

GOAL::Code_Lines &
GOAL::Code_Line_Builder::get_lines ()
{
  return code_lines_;
}

void GOAL::Code_Line_Builder::Visit_KeyboardConnection(const KeyboardConnection& target)
{

}

void GOAL::Code_Line_Builder::Visit_VariableRef(const VariableRef& target)
{
	//AfxMessageBox(name.c_str ());
}

void GOAL::Code_Line_Builder::Visit_Plus(const Plus& target)
{
  source_ << " + ";
}

void GOAL::Code_Line_Builder::Visit_Divide(const Divide& target)
{
  source_ << " / ";
}

void GOAL::Code_Line_Builder::Visit_Times(const Times& target)
{
  source_ << " * ";
}

void GOAL::Code_Line_Builder::Visit_Minus(const Minus& target)
{
  source_ << " - ";
}

void GOAL::Code_Line_Builder::Visit_Modulus(const Modulus& target)
{
  source_ << " % ";
}

void GOAL::Code_Line_Builder::Visit_Function(const Function& target)
{
  ++tabs_;
  
  

  --tabs_;
}

void GOAL::Code_Line_Builder::Visit_FunctionCall(const FunctionCall& target)
{
  GOAL::Function function = GOAL::Function::Cast (target.ref ());

  source_ << target.name ().operator std::string () <<
    "." << function.name ().operator std::string () << "()";
}

void GOAL::Code_Line_Builder::Visit_FunctionCallLine(const GOAL::Grammar::FunctionCallLine& target)
{  
  std::string tab_string (tabs_ * 2, ' ');

  source_ << tab_string;

  this->Visit_FunctionCall (target.object);
  source_ << ";\n";
}

void GOAL::Code_Line_Builder::Visit_Class(const Class& target)
{

  //***********************************************************
/*

  int oldTabs = tabs_;
  
  std::string directory (
    GOAL::getParentPath (target.getPath2 (".",false)));
  
  GOAL::writeModulePathToStream (source_, directory, type_, tabs_);

  std::string tab_string (" ",tabs_ * 2);

  source_ << tab_string << "class " << target.name () << "\n";
  source_ << tab_string << "{\n";
  source_ << tab_string << "public:\n";

  ++tabs_;
  


  --tabs_;
  source_ << tab_string << "};\n";

  for (--tabs_; tabs_ > oldTabs; --tabs_)
    {
      source_ << std::string (" ",tabs_ * 2) << "}\n";
    }

  tabs_ = oldTabs;
*/
}

void GOAL::Code_Line_Builder::Visit_Module(const Module& target)
{

}

void GOAL::Code_Line_Builder::Visit_CodeAdapter(const CodeAdapter& target)
{
  ++tabs_;
  std::set <MgaObject> mgaObjects = target.MgaObject_kind_children ();
  std::set <MgaObject>::iterator j = mgaObjects.begin ();

  for (; j != mgaObjects.end (); ++j)
    {
      // We discard all connections. 
      if (grammar_.isConnection (j->type ()))
        continue;

      this->code_lines_.insert (*j);
    }

  code_lines_.prune ();

  // sort lines by y then x
  Code_Lines sorted (code_lines_.getSorted ());

  for (int count = 0; count < this->get_num_lines (); ++count)
    {
      // for each line, sort by x
      Code_Line sortedLine (sorted[count].getSorted ());
      GOAL::Grammar::Info info (sortedLine);

      if (grammar_.isAssignmentLine (sortedLine,info))
        {
          Code_Line::iterator assign = info.pos;
          if (grammar_.isConsolePrintout (sortedLine,info))
            {
              GOAL::Grammar::ConsolePrint print (sortedLine,info.pos);
              this->Visit_ConsolePrint (print);
            }
          else if(grammar_.isInputLine (sortedLine,info))
            {
              GOAL::Grammar::InputLine input (sortedLine,info.pos);
              this->Visit_InputLine (input);
            }
          else 
            {
              GOAL::Grammar::Assignment assignment (sortedLine,assign);
              this->Visit_AssignmentLine (assignment);
            }
        }
      else
        {
          if (grammar_.isFunctionCall (sortedLine,info))
            {
              GOAL::Grammar::FunctionCallLine function;
              function.object = GOAL::FunctionCall::Cast (*info.pos);
              this->Visit_FunctionCallLine (function);
            }
        }
    }

  /*

  std::string objects;
  Code_Lines un_sorted (this->get_lines ());

  objects = "Before sorting\n\n";

  for (unsigned int count = 0; count < un_sorted.size (); ++count)
    {
      objects += "\nObjects in line are: ";
      objects += un_sorted[count].getSorted ().output ();
    }

  objects += "\n\nAfter sorting\n\n";

  Code_Lines sorted (un_sorted.getSorted ());

  for (unsigned int count = 0; count < sorted.size (); ++count)
    {
      objects += "\nObjects in line are: ";
      objects += sorted[count].getSorted ().output ();
    }

  AfxMessageBox(objects.c_str ());
*/

  --tabs_;
}

void GOAL::Code_Line_Builder::Visit_AssignmentLine(
  const GOAL::Grammar::Assignment& target)
{
  //target.assign_;
  std::string tab_string (tabs_ * 2, ' ');

  source_ << tab_string;

  GOAL::Code_Line::iterator lhs = target.code_line_.begin ();
  for (; lhs != target.assign_; ++lhs)
    {
      GOAL::Grammar::LhsElement element;
      element.object = *lhs;
      this->Visit_LhsElement (element);
    }

  this->Visit_Assignment (GOAL::Assignment::Cast (*target.assign_));

  GOAL::Code_Line::iterator rhs = target.assign_ + 1;

  for (; rhs != target.code_line_.end (); ++rhs)
    {
      // visit the other elements in the code_line
      Grammar::RhsElement element;
      element.object = *rhs;
      this->Visit_RhsElement (element);
    }

  source_ << ";\n";
}

void GOAL::Code_Line_Builder::Visit_InputElement(
  const GOAL::Grammar::InputElement& target)
{
  std::string type = target.object.type ().name (). operator std::string ();
  if      ("Keyboard" == type)
    {
      this->Visit_Keyboard (GOAL::Keyboard::Cast (target.object));
    }
  else if ("File" == type)
    {
      this->Visit_File (GOAL::File::Cast (target.object));
    }
}

void GOAL::Code_Line_Builder::Visit_LhsElement(
  const GOAL::Grammar::LhsElement& target)
{
  std::string type = target.object.type ().name (). operator std::string ();
  if      ("Variable" == type)
    {
      this->Visit_Variable (GOAL::Variable::Cast (target.object));
    }
  else if ("Brackets" == type)
    {
      this->Visit_Brackets (GOAL::Brackets::Cast (target.object));
    }
}

void GOAL::Code_Line_Builder::Visit_RhsElement(
  const GOAL::Grammar::RhsElement& target)
{
  std::string type = target.object.type ().name (). operator std::string ();
  if      ("Plus" == type)
    {
      this->Visit_Plus (GOAL::Plus::Cast (target.object));
    }
  else if ("Minus" == type )
    {
      this->Visit_Minus (GOAL::Minus::Cast (target.object));
    }
  else if ("Variable" == type )
    {
      this->Visit_Variable (GOAL::Variable::Cast (target.object));
    }
  else if ("Brackets" == type )
    {
      this->Visit_Brackets (GOAL::Brackets::Cast (target.object));
    }
  else if ("Const" == type )
    {
      this->Visit_Const (GOAL::Const::Cast (target.object));
    }
  else if ("FunctionCall" == type )
    {
      this->Visit_FunctionCall (GOAL::FunctionCall::Cast (target.object));
    }
}



void GOAL::Code_Line_Builder::Visit_InputLine(
  const GOAL::Grammar::InputLine& target)
{
  std::string tab_string (tabs_ * 2, ' ');

  source_ << tab_string;

  GOAL::Code_Line_Iterator i = target.assign_ + 1;

  GOAL::Grammar::InputElement input;
  input.object = *i;


  this->Visit_InputElement (input);

  for (i = target.code_line_.begin (); i != target.assign_; ++i)
    {
      // visit the other elements in the code_line
      Grammar::RhsElement element;
      element.object = *i;
      source_ << " >> ";
      this->Visit_RhsElement (element);
    }


  source_ << ";\n";
}

void GOAL::Code_Line_Builder::Visit_ConsolePrint(
  const GOAL::Grammar::ConsolePrint& target)
{
  std::string tab_string (tabs_ * 2, ' ');

  source_ << tab_string;

  // this will not really be necessary once I implement the
  // java and c++ visitors
  if (this->type_ == "C++")
    {
      source_ << "std::cout";
    }
  else if(this->type_ == "Java")
    {
      source_ << "System.out.print(\"";
    }

  //target.assign_;
  GOAL::Code_Line_Iterator i = target.assign_;

  for (++i; i != target.code_line_.end (); ++i)
    {
      // visit the other elements in the code_line
      Grammar::RhsElement element;
      element.object = *i;
      source_ << " << ";
      this->Visit_RhsElement (element);
    }

  if (this->type_ == "C++")
    {
      source_ << ";\n";
    }
  else if(this->type_ == "Java")
    {
      source_ << "\");\n";
    }

}

void GOAL::Code_Line_Builder::Visit_AccumulatorBase(const AccumulatorBase& target)
{

}

void GOAL::Code_Line_Builder::Visit_ModuleAdapter(const ModuleAdapter& target)
{
  std::string directory ("source/" + 
    GOAL::convertToDirectory (target.getPath2 (".",false)));
  
  AfxMessageBox(directory.c_str ());

  GOAL::createDirectory (directory);

  // since Modules derive from ModuleAdapters, this should cover it
  std::set <ModuleAdapter> moduleAdapters = target.ModuleAdapter_kind_children ();
  std::set <ModuleAdapter>::iterator i = moduleAdapters.begin ();

  for (; i != moduleAdapters.end (); ++i)
    {
      this->Visit_ModuleAdapter (*i);
    }

  // since Class derives from ClassAdapter, this should cover it
  std::set <ClassAdapter> classAdapters = target.ClassAdapter_kind_children ();
  std::set <ClassAdapter>::iterator j = classAdapters.begin ();

  for (; j != classAdapters.end (); ++j)
    {
      Code_Line_Builder builder (*j);
      builder.Visit_ClassAdapter (*j);
    }
}

void GOAL::Code_Line_Builder::Visit_ClassAdapter(const ClassAdapter& target)
{

  int oldTabs = tabs_;
  
  std::string directory (
    GOAL::getParentPath (target.getPath2 (".",false)));
  
  GOAL::writeModulePathToStream (source_, directory, type_, tabs_);

  std::string tab_string (tabs_ * 2, ' ');

  source_ << tab_string << "class " << target.name () << "\n";
  source_ << tab_string << "{\n";
  source_ << tab_string << "public:\n";

  ++tabs_;
  


  --tabs_;
  source_ << tab_string << "};\n";

  for (--tabs_; tabs_ >= oldTabs; --tabs_)
    {
      std::string new_tab_str (tabs_ * 2, ' ');

      source_ << new_tab_str << "}\n";
    }

  tabs_ = oldTabs;
}

void GOAL::Code_Line_Builder::Visit_Not(const Not& target)
{
  source_ << " ! ";
}

void GOAL::Code_Line_Builder::Visit_ConditionsAdapter(const ConditionsAdapter& target)
{
}

void GOAL::Code_Line_Builder::Visit_Assignment(const Assignment& target)
{
  source_ << " = ";
}

void GOAL::Code_Line_Builder::Visit_File(const File& target)
{
  source_ << target.name ().operator std::string ();
}

void GOAL::Code_Line_Builder::Visit_Console(const Console& target)
{
  
}

void GOAL::Code_Line_Builder::Visit_Keyboard(const Keyboard& target)
{
  source_ << "std::cin";
}

void GOAL::Code_Line_Builder::Visit_AssignmentConnection(const AssignmentConnection& target)
{
}

void GOAL::Code_Line_Builder::Visit_Brackets(const Brackets& target)
{
  source_ << " [] ";
}

void GOAL::Code_Line_Builder::Visit_IndexConnection(const IndexConnection& target)
{
}

void GOAL::Code_Line_Builder::Visit_NonModifyingConnection(const NonModifyingConnection& target)
{
}

void GOAL::Code_Line_Builder::Visit_For(const For& target)
{
  source_ << " for (;;); ";
}

void GOAL::Code_Line_Builder::Visit_While(const While& target)
{
  source_ << " while (); ";
}

void GOAL::Code_Line_Builder::Visit_If(const If& target)
{
  source_ << " if (); ";
}

void GOAL::Code_Line_Builder::Visit_PlusEquals(const PlusEquals& target)
{
  source_ << " += ";
}

void GOAL::Code_Line_Builder::Visit_DivideEquals(const DivideEquals& target)
{
  source_ << " /= ";
}

void GOAL::Code_Line_Builder::Visit_MinusEquals(const MinusEquals& target)
{
  source_ << " -= ";
}

void GOAL::Code_Line_Builder::Visit_TimesEquals(const TimesEquals& target)
{
  source_ << " *= ";
}

void GOAL::Code_Line_Builder::Visit_Increment(const Increment& target)
{
  source_ << " ++ ";
}

void GOAL::Code_Line_Builder::Visit_Decrement(const Decrement& target)
{
  source_ << " -- ";
}

void GOAL::Code_Line_Builder::Visit_And(const And& target)
{
  source_ << " && ";
}

void GOAL::Code_Line_Builder::Visit_Or(const Or& target)
{
  source_ << " || ";
}

void GOAL::Code_Line_Builder::Visit_OutputConnection(const OutputConnection& target)
{
}

void GOAL::Code_Line_Builder::Visit_ArrayAssignmentConnection(const ArrayAssignmentConnection& target)
{
}

void GOAL::Code_Line_Builder::Visit_AssignmentRHSConnection(const AssignmentRHSConnection& target)
{
}

void GOAL::Code_Line_Builder::Visit_ArgumentConnection(const ArgumentConnection& target)
{
}

void GOAL::Code_Line_Builder::Visit_Array(const Array& target)
{
}

void GOAL::Code_Line_Builder::Visit_Return(const Return& target)
{
  source_ << " function return ";
}

void GOAL::Code_Line_Builder::Visit_Argument(const Argument& target)
{
  source_ << " function arg ";
}

void GOAL::Code_Line_Builder::Visit_Const(const Const& target)
{
  target.Value ().operator std::string ();

  bool isString = false;

  std::set <ClassRef> classes = target.ClassRef_kind_children ();
  std::set <ClassRef>::iterator i = classes.begin ();

  if (classes.size () > 0 )
    {
      GOAL::Class ref = i->ref ();
      
      if ("String" == ref.name ().operator std::string ())
        {
          isString = true;
          source_ << "\"";
        }
    }


  source_ << target.Value ().operator std::string ();

  if (isString)
    source_ << "\"";
}

void GOAL::Code_Line_Builder::Visit_ClassMember(const ClassMember& target)
{
}

void GOAL::Code_Line_Builder::Visit_ClassRef(const ClassRef& target)
{
}

void GOAL::Code_Line_Builder::Visit_Variable(const Variable& target)
{
  source_ << target.name ();
}

void GOAL::Code_Line_Builder::Visit_RootFolder(const RootFolder& target)
{
  // check through the list for code line capable adapters

  std::set <CodeAdapter> codeAdapters = target.CodeAdapter_kind_children ();
  std::set <CodeAdapter>::iterator i = codeAdapters.begin ();

  for (; i != codeAdapters.end (); ++i)
    {
      Code_Line_Builder builder (*i);
      builder.Visit_CodeAdapter (*i);
    }

  std::set <ModuleAdapter> moduleAdapters = target.ModuleAdapter_kind_children ();
  std::set <ModuleAdapter>::iterator j = moduleAdapters.begin ();

  for (; j != moduleAdapters.end (); ++j)
    {
      this->Visit_ModuleAdapter (*j);
    }



}

void GOAL::Code_Line_Builder::Visit_CommonConnection(const CommonConnection& target)
{

}

void GOAL::Code_Line_Builder::Visit_Object(const Udm::Object& target)
{
}



#endif // CODE_LINES_BUILDER_CPP
